﻿using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Windows.Automation;
using ProdControls.Interfaces;
using ProdControls.UIAPatterns;
using ProdControls.Utility;
using ProdControls.Verification;
using ProdLogging;

namespace ProdControls.Bridges
{
    internal static class MenuBridge
    {
        private static int _ctr;

        /// <summary>
        ///   Selects the menu item.
        /// </summary>
        /// <param name="extension"> The extension. </param>
        /// <param name="control"> The control. </param>
        /// <param name="itemPath"> The item path, with each item down the path as an item in the list </param>
        /// <remarks>
        ///   Menu item text MUST be exact (but not case-sensitive). 'Open' will not match an item 'Open...' but will match 'open'
        /// </remarks>
        internal static void SelectMenuItemHook(this IExpandCollapse extension, BaseProdControl control,
                                                string[] itemPath)
        {
            _ctr = 0;
            UiaSelectMenuItem(control, itemPath);
        }

        private static void UiaSelectMenuItem(BaseProdControl control, string[] itemPath)
        {
            AutomationElementCollection menuItems = UiaGetMenuItems(control);
            AutomationEventVerifier.Register(new EventRegistrationMessage(control, InvokePattern.InvokedEvent));
            LogController.ReceiveLogMessage(new LogMessage(string.Join(",", itemPath)));
            ExpandMenuItem(menuItems, itemPath);
        }

        /// <summary>
        ///   Gets the menu items contained in the current Menu Bar.
        /// </summary>
        /// <param name="extension"> The extension. </param>
        /// <param name="control"> The control. </param>
        /// <returns> </returns>
        internal static AutomationElementCollection GetMenuItemsHook(this IExpandCollapse extension,
                                                                     BaseProdControl control)
        {
            return UiaGetMenuItems(control);
        }

        private static AutomationElementCollection UiaGetMenuItems(BaseProdControl control)
        {
            AutomationElementCollection retVal = control.UiaElement.FindAll(TreeScope.Children,
                                                                            new PropertyCondition(
                                                                                AutomationElement.
                                                                                    ControlTypeProperty,
                                                                                ControlType.MenuItem));
            Collection<object> retColl = Utilities.AutomationCollToObjectList(retVal);
            LogController.ReceiveLogMessage(new LogMessage(string.Join("Items", retColl)));
            return retVal;
        }

        /// <summary>
        ///   Invokes the menu item by accelerator key.
        /// </summary>
        /// <param name="extension"> The extension. </param>
        /// <param name="control"> The control. </param>
        /// <remarks>
        ///   Retrieves accelerator keys from supplied controls properties
        /// </remarks>
        internal static void InvokeByAcceleratorKeyHook(this IInvoke extension, BaseProdControl control)
        {
            UiaInvokeByAcceleratorKey(control);
        }

        /// <summary>
        ///   Invokes the menu item by accelerator key.
        /// </summary>
        /// <param name="extension"> The extension. </param>
        /// <param name="control"> The control. </param>
        /// <param name="keyCombination"> The key combination. </param>
        /// <remarks>
        ///   Converts from format of "Shift+CTRL+Y" into "+^(Y)"
        /// </remarks>
        internal static void InvokeByAcceleratorKeyHook(this IInvoke extension, BaseProdControl control,
                                                        string keyCombination)
        {
            UiaInvokeByAcceleratorKey(control, keyCombination);
        }

        private static void UiaInvokeByAcceleratorKey(BaseProdControl control)
        {
            if (control.UiaElement.Current.AcceleratorKey.Length == 0)
            {
                return;
            }
            string cleaned = Utilities.ConvertStringToSendKey(control.UiaElement.Current.AcceleratorKey);
            control.SetFocus();
            AutomationEventVerifier.Register(new EventRegistrationMessage(control, InvokePattern.InvokedEvent));
            LogController.ReceiveLogMessage(new LogMessage(cleaned));
            Utilities.SendKeysSetText(control.UiaElement, cleaned);
        }

        private static void UiaInvokeByAcceleratorKey(BaseProdControl control, string keyCombination)
        {
            string cleaned = Utilities.ConvertStringToSendKey(keyCombination);
            control.SetFocus();
            AutomationEventVerifier.Register(new EventRegistrationMessage(control, InvokePattern.InvokedEvent));
            LogController.ReceiveLogMessage(new LogMessage(cleaned));
            Utilities.SendKeysSetText(control.UiaElement, cleaned);
        }

        /// <summary>
        ///   Invokes the menu item by access key.
        /// </summary>
        /// <param name="extension"> The extension. </param>
        /// <param name="control"> The menu item element. </param>
        internal static void InvokeByAccessKeyHook(this IInvoke extension, BaseProdControl control)
        {
            UiaInvokeByAccessKey(control);
        }

        /// <summary>
        ///   Invokes the menu item by access key.
        /// </summary>
        /// <param name="extension"> The extension. </param>
        /// <param name="control"> The menu item element. </param>
        /// <param name="keyCombination"> The key combination. </param>
        internal static void InvokeByAccessKeyHook(this IInvoke extension, BaseProdControl control,
                                                   string keyCombination)
        {
            UiaInvokeByAccessKey(control, keyCombination);
        }

        private static void UiaInvokeByAccessKey(BaseProdControl control)
        {
            if (control.UiaElement.Current.AccessKey.Length == 0)
            {
                return;
            }
            string cleaned = Utilities.ConvertStringToSendKey(control.UiaElement.Current.AccessKey);
            control.SetFocus();
            AutomationEventVerifier.Register(new EventRegistrationMessage(control, InvokePattern.InvokedEvent));
            LogController.ReceiveLogMessage(new LogMessage(cleaned));
            Utilities.SendKeysSetText(control.UiaElement, cleaned);
        }

        private static void UiaInvokeByAccessKey(BaseProdControl control, string keyCombination)
        {
            string cleaned = Utilities.ConvertStringToSendKey(keyCombination);
            control.SetFocus();
            AutomationEventVerifier.Register(new EventRegistrationMessage(control, InvokePattern.InvokedEvent));
            LogController.ReceiveLogMessage(new LogMessage(cleaned));
            Utilities.SendKeysSetText(control.UiaElement, cleaned);
        }

        /* helper functions */

        /// <summary>
        ///   Gets the child menu items.
        /// </summary>
        /// <param name="item"> The parent MenuItems. </param>
        /// <returns> A collection of MenuItems </returns>
        private static AutomationElementCollection GetChildMenuItems(AutomationElement item)
        {
            AutomationElementCollection ret = item.FindAll(TreeScope.Descendants,
                                                           new PropertyCondition(
                                                               AutomationElement.ControlTypeProperty,
                                                               ControlType.MenuItem));
            return ret;
        }

        /// <summary>
        ///   Expands the menu item.
        /// </summary>
        /// <param name="menuItems"> The menu items. </param>
        /// <param name="itemPath"> The item path. </param>
        private static void ExpandMenuItem(IEnumerable menuItems, string[] itemPath)
        {
            foreach (AutomationElement item in menuItems)
            {
                /* Expand top menu */
                string name = item.Current.Name;
                if (string.Compare(name, itemPath[_ctr], true, CultureInfo.CurrentCulture) != 0) continue;

                if (CommonUIAPatternHelpers.CheckPatternSupport(ExpandCollapsePattern.Pattern, item) != null)
                {
                    ExpandCollapseHelper.Expand(item);

                    AutomationElementCollection items = GetChildMenuItems(item);
                    _ctr++;
                    ExpandMenuItem(items, itemPath);
                }

                if (CommonUIAPatternHelpers.CheckPatternSupport(InvokePattern.Pattern, item) == null) return;
                InvokePatternHelper.Invoke(item);
            }
        }

        /// <summary>
        ///   Gets the main menu bar attached to the target window.
        /// </summary>
        /// <param name="window"> The target window. </param>
        /// <returns> The main menu bar as an AutomationElement </returns>
        private static AutomationElement GetMainMenuBar(AutomationElement window)
        {
            AutomationElementCollection menuBars = GetMenuBars(window);

            if (menuBars.Count == 0)
            {
                throw new InvalidOperationException("Could Not find Menu Bar");
            }
            return menuBars[0];
        }

        /// <summary>
        ///   Gets all the MenuBars attached to a window.
        /// </summary>
        /// <param name="window"> The target window. </param>
        /// <returns> The menu bars as an AutomationElementCollection </returns>
        private static AutomationElementCollection GetMenuBars(AutomationElement window)
        {
            return window.FindAll(TreeScope.Descendants,
                                  new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuBar));
        }
    }
}